package minidb.index;


import minidb.record.*;
import minidb.buffer.*;
import static minidb.type.Const.*;
import minidb.type.*;


public class bTreeIndex {

	bTreePage btree;
	TableInfo tab;
	RecordPage rec;
	String field;
	Constant SearchKey;
	
	public bTreeIndex(TableInfo tab, RecordPage rec ){
		btree = null;
		this.tab = tab;
		this.rec = rec;
		field = "";
	}
	
	public bTreeIndex(){
		btree = null;
		this.tab = null;
		this.rec = null;
		field = "";
	}
	public void rewind(){
		synchronized(tab.lockindex){
			tab.indexRead++;
		}
		if(btree != null)
			btree.toFirst();
		synchronized(tab.lockindex){
			tab.indexRead--;
		}
		
	}
	
	public Boolean isOnly(){
		return btree.isOnly();
	}
	public void index(String fieldName){
		if(field.compareTo(fieldName)==0){
			btree.rewind();
			return;
		}
		if(btree != null){
			btree.close();
		}
		int next = 0;
		String tmp;
		synchronized(tab.lockindex){
			tab.indexRead++;
		}
		Buffer buf = bufpool.pin(tab.indexPage());
		next = buf.getInt(NEXT_PAGE);
		if(next == 0){
			bufpool.unpin(buf);
			synchronized(tab.lockindex){
				tab.indexRead--;
			}
			throw new RuntimeException("NO SUCH INDEX: " + fieldName);
		}
		bufpool.unpin(buf);
		buf = bufpool.pin(next);
		tmp = buf.getString(INT_SIZE*5);
		while(tmp.compareTo(fieldName)!=0){
			next = buf.getInt(NEXT_PAGE);
			if(next==0){
				bufpool.unpin(buf);
				synchronized(tab.lockindex){
					tab.indexRead--;
				}
				throw new RuntimeException("NO SUCH INDEX: " + fieldName);
			}
			bufpool.unpin(buf);
			buf = bufpool.pin(next);
			tmp = buf.getString(INT_SIZE*5);
		}
		field = tmp;
		btree = new bTreePage(buf.pageNum(), rec, tab);
		bufpool.unpin(buf);
		synchronized(tab.lockindex){
			tab.indexRead--;
		}
	}
	
	public RID beforFirst(Constant val){
		this.SearchKey = val;
		if(btree==null)
			return null;
		else{
			synchronized(tab.lockindex){
				tab.indexRead++;
			}
			RID rid = btree.beforeFirst(val);
			synchronized(tab.lockindex){
				tab.indexRead--;
			}
			return rid;
		}
	}
	
	public void Insert(Record r, RID rid){
		while(true){
			synchronized(tab.lockindex){
				if(tab.indexRead==0){
					Buffer buf = bufpool.pin(tab.indexPage());
					int next = buf.getInt(NEXT_PAGE);
					while(next!=0){
						bufpool.unpin(buf);
						buf = bufpool.pin(next); 
						btree = new bTreePage(next, rec, tab);
						try{
							btree.insert(r.get(btree.fieldName()), rid);
						}catch(Exception e){
							next = buf.getInt(PRE_PAGE);
							bufpool.unpin(buf);
							buf = bufpool.pin(next);
							while(next!=tab.indexPage()){
								btree = new bTreePage(next, rec, tab);
								btree.delete(r.get(btree.fieldName()), rid);
								next = buf.getInt(PRE_PAGE);
								bufpool.unpin(buf);
								buf = bufpool.pin(next);
							}
							bufpool.unpin(buf);
							close();
							throw new RuntimeException(e.getMessage());
						}
						next = buf.getInt(NEXT_PAGE);
					}
					bufpool.unpin(buf);
					break;
				}
			}
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			
		}
	}
	
	public boolean Insert(Constant val, RID r){
		while(true){
			synchronized(tab.lockindex){
				if(tab.indexRead==0){
					if(btree == null )
						return false;
					btree.insert(val, r);
					return true;
				}
			}
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		
	}
	
	public void Delete(Record r, RID rid){
		while(true){
			synchronized(tab.lockindex){
				if(tab.indexRead==0){
					Buffer buf = bufpool.pin(tab.indexPage());
					int next = buf.getInt(NEXT_PAGE);
					bufpool.unpin(buf);
					buf = bufpool.pin(next);
					while(next!=0){
						btree = new bTreePage(next, rec, tab);
						try{
							btree.delete(r.get(btree.fieldName()), rid);
						}catch(Exception e){
							System.out.println(e.getMessage());
						}
						next = buf.getInt(NEXT_PAGE);
						bufpool.unpin(buf);
						buf = bufpool.pin(next);
					}
					bufpool.unpin(buf); 
					break;
				}
			}
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
	public RID next(){
		synchronized(tab.lockindex){
			tab.indexRead++;
		}
		RID rid = btree.next();
		synchronized(tab.lockindex){
			tab.indexRead--;
		}
		return rid;
	}
	
	public boolean CreatIndex(String indexName, String fieldName, int isOnly){
		int newPage = 0;
		while(true){
			synchronized(tab.lockindex){
				if(tab.indexRead==0){
					if(new indexHeader().getIndex(indexName)!=0)
						throw new RuntimeException("INDEX HAS ALREADY EXIST: " + fieldName);
					newPage = dbFile.allocPage();
					int PageNum = dbFile.allocPage();
					
					new indexHeader().Insert(indexName, newPage);
					Buffer buf = bufpool.pin(tab.indexPage());
					int next = buf.getInt(NEXT_PAGE);
					buf.putInt(NEXT_PAGE, newPage);
					if(next!=0){
						bufpool.unpin(buf);
						buf = bufpool.pin(next);
						buf.putInt(PRE_PAGE, newPage);
					}					
					bufpool.unpin(buf);
					buf = bufpool.pin(newPage);
					buf.putInt(PRE_PAGE, tab.indexPage());
					buf.putInt(NEXT_PAGE, next);
					buf.putInt(INT_SIZE*4, PageNum);
					buf.putString(INT_SIZE*5, fieldName);
					buf.putInt(INT_SIZE*5+fieldName.length()+1, isOnly);
					bufpool.unpin(buf);
					buf = bufpool.pin(PageNum);
					buf.putInt(INT_SIZE * 5, 1);
					bufpool.unpin(buf);
					break;
				}
			}
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}	
		index(fieldName);
		rec.rewind();
		Record r;
		RID rid;
		while(rec.next()){	
			r = rec.get();
			rid = rec.Rid();
			if(Insert(r.get(fieldName), r.rid())==false){
				DeleteIndex(newPage);
				return false;
			}
			rec.Rid(rid);
		}
		return true;
	}
	
	public boolean DeleteIndex(int pageNum){
		while(true){
				if(tab.indexRead==0){
					Buffer buf = bufpool.pin(pageNum);
					int prev = buf.getInt(PRE_PAGE);
					int next = buf.getInt(NEXT_PAGE);
					btree = new bTreePage(pageNum);
					if(prev!=0){
						bufpool.unpin(buf);
						buf = bufpool.pin(prev);
						buf.putInt(NEXT_PAGE, next);
					}
					if(next!=0){
						bufpool.unpin(buf);
						buf = bufpool.pin(next);
						buf.putInt(PRE_PAGE, prev);
					}
					bufpool.unpin(buf);
					btree.DropPage();
					dbFile.deallocPage(pageNum);
					return true;
				}
			
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
	public void close(){
		rec.close();
		if(btree!=null)
			btree.close();
	}
}
